--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 3686bb6b74ace6831d09f161b17e374cf381128e
Parents : 964ef9b
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-04-23T02:28:16-05:00
feat(fetch_repository_wheels): add script to download bundled wheels for offline-first installs
Changes
Diff
diff --git a/scripts/build/fetch_repository_wheels.py b/scripts/build/fetch_repository_wheels.py
new file mode 100644
index 00000000..4aa65e3b
--- /dev/null
+++ b/scripts/build/fetch_repository_wheels.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+"""Download repository bundled wheels at build time (offline-first installs).
+
+Wheels are written to ``meshchatx/public/repository-server-bundled/bundled`` so they
+ship with the same artifact layout as the Vite output. At runtime,
+:class:`~meshchatx.src.backend.repository_server_manager.RepositoryServerManager`
+copies any missing ``*.whl`` files from that directory into each identity's
+``repository-server/bundled`` folder (no network required).
+
+Usage::
+
+ python3 scripts/build/fetch_repository_wheels.py [--dest DIR]
+
+Environment::
+
+ MESHCHATX_SKIP_REPOSITORY_WHEELS_FETCH If ``1``/``true``, exit without downloading.
+"""
+
+from __future__ import annotations
+
+import argparse
+import logging
+import os
+import sys
+from pathlib import Path
+
+DEFAULT_DEST = (
+ Path(__file__).resolve().parent.parent.parent
+ / "meshchatx"
+ / "public"
+ / "repository-server-bundled"
+ / "bundled"
+)
+
+
+def _is_truthy(value: str | None) -> bool:
+ return value is not None and value.strip().lower() in {"1", "true", "yes", "on"}
+
+
+def main(argv: list[str] | None = None) -> int:
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
+ if _is_truthy(os.environ.get("MESHCHATX_SKIP_REPOSITORY_WHEELS_FETCH")):
+ logging.info(
+ "Skipping repository wheels fetch (MESHCHATX_SKIP_REPOSITORY_WHEELS_FETCH)."
+ )
+ return 0
+
+ repo_root = Path(__file__).resolve().parent.parent.parent
+ rr = str(repo_root)
+ if rr not in sys.path:
+ sys.path.insert(0, rr)
+
+ parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
+ parser.add_argument(
+ "--dest",
+ type=Path,
+ default=DEFAULT_DEST,
+ help=f"Output directory (default: {DEFAULT_DEST})",
+ )
+ args = parser.parse_args(argv)
+ dest: Path = args.dest.resolve()
+
+ from meshchatx.src.backend.repository_server_manager import (
+ download_bundled_wheels_to_directory,
+ )
+
+ dest.mkdir(parents=True, exist_ok=True)
+ for old in dest.glob("*.whl"):
+ try:
+ old.unlink()
+ except OSError as e:
+ logging.warning("Could not remove %s: %s", old, e)
+
+ result = download_bundled_wheels_to_directory(dest, pip=None)
+ failed = result.get("failed") or {}
+ ok = result.get("downloaded") or []
+ if failed:
+ for pkg, msg in failed.items():
+ logging.error("%s: %s", pkg, msg[:500])
+ if ok:
+ logging.info("Repository wheels OK: %s", ", ".join(ok))
+ if not ok and failed:
+ return 1
+ if failed:
+ logging.warning(
+ "Repository wheels partial success (%d ok, %d failed).",
+ len(ok),
+ len(failed),
+ )
+ return 0
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────